package ferp.core.calc.scorer;

import ferp.core.calc.Calculator;
import ferp.core.game.Game;
import ferp.core.game.Settings;
import ferp.core.player.Player;
import ferp.core.player.Score;

/**
 * User: olegs
 * Date: 12/3/13 1:37 PM
 */
public abstract class Scorer
{
  // calculate balance
  public static void balance(Game g, Settings settings, Calculator c)
  {
    // obtain mountain delta
    int nf = c.getNormalizeFactor();

    for (Player p : g.players())
      g.getScore(p.id()).mountain.delta = nf * (settings.poolSize - g.getScore(p.id()).pool.current);

    // find minimal mountain
    long minMountain = Integer.MAX_VALUE;
    for (Player p : g.players())
      if (g.getScore(p.id()).mountain.eventual() < minMountain)
        minMountain = g.getScore(p.id()).mountain.eventual();

    // amnesty + write final whists
    for (Player p : g.players())
    {
      g.getScore(p.id()).mountain.delta -= minMountain;

      if (g.getScore(p.id()).mountain.eventual() > 0)
      {
        long whists = 10 * g.getScore(p.id()).mountain.eventual() / Game.PLAYERS;

        for (Player other = g.next(p); other != p; other = g.next(other))
          g.getScore(other.id()).whists[p.id()].delta = whists;
      }
      else
      {
        // zero the eventual mountain value
        g.getScore(p.id()).mountain.delta = -g.getScore(p.id()).mountain.current;

        // zero whist delta on player who has 0 eventual mountain (otherwise some player might hold delta value from previous set's calculation)
        for (Player other = g.next(p); other != p; other = g.next(other))
          g.getScore(other.id()).whists[p.id()].delta = 0;
      }
    }

    // calculate balance for each player
    for (Player p : g.players())
    {
      Score score = g.getScore(p.id());
      long old = score.balance2.current;

      score.balance2.current = 0;

      for (Player other = g.next(p); other != p; other = g.next(other))
        score.balance2.current += score.whists[other.id()].eventual() - g.getScore(other.id()).whists[p.id()].eventual();

      // update balance delta
      score.balance2.delta = score.balance2.current - old;

      // update statistics
      g.statistics.score[p.id()] = score.balance2.current;
    }
  }

  public void score(Game g, Settings settings, Calculator c)
  {
    settle(g);
    update(g, settings, c);
    balance(g, settings, c);
    statistics(g);
  }

  // write corresponding score (passing, misere, etc.)
  protected abstract void update(Game g, Settings settings, Calculator c);

  // update corresponding statistics (passing, misere, etc.)
  protected abstract void statistics(Game g);

  // mark last scores as not changed
  private void settle(Game g)
  {
    for (Player p : g.players())
      g.getScore(p.id()).settle();
  }
}
